﻿using System;
using System.Diagnostics;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using Sripirom.ATS.Core.Configuration;
using Sripirom.ATS.Core.ExceptionHandler;
using Sripirom.ATS.Core.Logging;
using Sripirom.ATS.Core.ServiceModel.Dispatcher;
using Sripirom.ATS.Core.UnitOfWork;
using Sripirom.ATS.Model.ServiceLog;
using StructureMap;

namespace Sripirom.ATS.Services.OperationDispatchers
{
    public abstract class AbsOperationInvoker<TChannelRequest, TChannelReply, TOperationData>
        where TOperationData : IOperationData<TChannelRequest, TChannelReply>
    {
        private IUnitOfWork _uow;
        private IServiceTransactionLogRepository _serviceTransactioryLogRepository;
        #region Constructor
        private IOperationInvoker _invoker;
        private String _operationName;
        protected Boolean _messageIsInValid;
        protected readonly DispatchOperation _operation;
        public AbsOperationInvoker(IOperationInvoker baseInvoker, DispatchOperation operation)
        {
            LoggerFactory.CreateLog().LogInfo("Constructor BaseOperationInvoker");
            _uow = ObjectFactory.GetInstance<IUnitOfWork>();
            _serviceTransactioryLogRepository = ObjectFactory.GetInstance<IServiceTransactionLogRepository>();
            _invoker = baseInvoker;
            _operationName = operation.Name;
            _operation = operation;

        }
        #endregion

        #region Properties

        public Int32 SysId { get; set; }
        public String ServiceDesc { get; set; }
        public String ServiceName { get; set; }
        
        
        #endregion

        #region Business Command Process

        protected abstract ServiceActivityLog ValidateMessage(TOperationData operationData);

        protected abstract ServiceActivityLog BuildInstruction(TOperationData operationData);

        protected abstract ServiceActivityLog ValidateTransaction(TOperationData operationData);

        protected abstract ServiceActivityLog BuildCommand(TOperationData operationData);

        protected abstract ServiceActivityLog ProcessTransaction(TOperationData operationData);

        #endregion

        #region Service Operation Process
 
        public MessageException MessageException
        {
            get
            {
                return null;// ((ValidatingParameterInspector)_operation.ParameterInspectors[0]).MessageException;
            }
        }
        

        public object Execution(object instance, TOperationData operationData) 
        {
            LoggerFactory.CreateLog().LogInfo(String.Format("I:{0}.{1}", instance.ToString(), _operationName));

            Stopwatch sw = new Stopwatch();
        
            try
            {

                LoggerFactory.CreateLog().LogInfo(String.Format("FundTransfer : XmlString = {0}", ""));

                // ต้องเช็ค response หลัง Execute ก่อน
                // LoggerFactory.CreateLog().LogInfo("FundTransfer : Result = " + dataResponse.Result);
                //isSuccess = dataResponse.Result == "0";
                LoggerFactory.CreateLog().LogInfo("I:Execute");
                //LogDebug("XML : " + request.XmlString);
                sw.Start();
                ServiceTransactionLog serviceTransactionLog = null;
                string sysServiceTransactionId = "SysServiceTransactionId";
                if (OperationContext.Current.IncomingMessageProperties.ContainsKey(sysServiceTransactionId))
                {
                    Decimal sysId = (Decimal)OperationContext.Current.IncomingMessageProperties[sysServiceTransactionId];
                    serviceTransactionLog = _serviceTransactioryLogRepository.FindBy(sysId);
                }
                // 1.Validate Message
               
                if (serviceTransactionLog.AddActivityLog(ValidateMessage(operationData)))                   
                {
                    _serviceTransactioryLogRepository.Save(serviceTransactionLog);
                    _uow.Commit();
                }
                // 2.Prepare And Instruction
                if (serviceTransactionLog.AddActivityLog(BuildInstruction(operationData)))                  
                {
                    _serviceTransactioryLogRepository.Save(serviceTransactionLog);
                    _uow.Commit();
                }
                // 3.Validate Transaction
                if (serviceTransactionLog.AddActivityLog(ValidateTransaction(operationData)))                
                {
                    _serviceTransactioryLogRepository.Save(serviceTransactionLog);
                    _uow.Commit();
                }
                // 4.Pre - Execute
                if (serviceTransactionLog.AddActivityLog(BuildCommand(operationData)))                      
                {
                    _serviceTransactioryLogRepository.Save(serviceTransactionLog);
                    _uow.Commit();
                }
                // 5.Execute
                if (serviceTransactionLog.AddActivityLog(ProcessTransaction(operationData)))                
                {
                    _serviceTransactioryLogRepository.Save(serviceTransactionLog);
                    _uow.Commit();
                }
                    
                // For post Execute ex: write Log Execute time 

                sw.Stop();
                LoggerFactory.CreateLog().LogInfo(String.Format("Time of travel: {0}", sw.Elapsed));

            }
            catch (MessageException ex)
            {
                operationData.CreateMessageException(ex);
                LoggerFactory.CreateLog().LogInfo("FundTransfer : Result = Exception");
            }
            catch (Exception ex)
            {
                operationData.CreateMessageException(ex);
                LoggerFactory.CreateLog().LogError(String.Format("{0} : Result = Exception (" + ex.Message + ")", _operationName), ex);
            }
            finally
            {
                //operationData.UpdateServiceOut();// SaveServiceTransactionLog(operationData);
                LoggerFactory.CreateLog().LogInfo(String.Format("O:{0}.{1}", instance.ToString(), _operationName));
            }

            return operationData.GetChannelReply();
        }


        #endregion

        #region Imprement IOperationInvoker
        public object[] AllocateInputs()
        {
            LoggerFactory.CreateLog().LogInfo("-BaseOperationInvoker.AllocateInputs");
           
           return  _invoker.AllocateInputs();
        }

        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {

            outputs = new object[] { };
            TOperationData operationData;
            if (inputs[0] == null)
            {
                operationData = (TOperationData)System.Activator.CreateInstance(typeof(TOperationData), null);

                return operationData.CreateMessageException(new MessageException(Errors.INVALID_REQUEST_MESSAGE));
            }


            operationData = (TOperationData)System.Activator.CreateInstance(typeof(TOperationData), (TChannelRequest)inputs[0]);
            //operationData.SaveServiceIn();
            if (MessageException != null)
            {
                LoggerFactory.CreateLog().Debug("Invoker MessageIsInvalid = true");

                return operationData.CreateMessageException(MessageException);

            }

            return Execution(instance, operationData);
        }

        public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
        {
            LoggerFactory.CreateLog().LogInfo("-BaseOperationInvoker.InvokeBegin");
            throw new NotImplementedException();
        }

        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
        {
            LoggerFactory.CreateLog().LogInfo("-BaseOperationInvoker.InvokeEnd");
            throw new NotImplementedException();
        }

        public bool IsSynchronous
        {
            get {
               LoggerFactory.CreateLog().LogInfo("-BaseOperationInvoker.IsSynchronous");
                return true; }
        }

        #endregion

     
    }

    
}